/*
 * $Id: content.c 240 2008-04-07 00:22:48Z jasta $
 *
 * Copyright (C) 2008 Josh Guilfoyle <jasta@devtcg.org>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 */

#include "main.h"

#include <errno.h>

#include <libsoup/soup-uri.h>
#include <libsoup/soup-server.h>
#include <libsoup/soup-server-message.h>

#include "db.h"

/*****************************************************************************/

static char *request_to_file(const char *path)
{
	size_t pfx = sizeof("/content/") - 1;
	char *ret;
	char *end;
	guint64 id;
	sqlite3_stmt *stmt;
	int rc;

	assert(path != NULL);

	if (strncmp(path, "/content/", pfx) != 0)
		return NULL;

	path += pfx;

	id = g_ascii_strtoull(path, &end, 10);

	if (path == end)
		return NULL;

	rc = sqlite3_prepare(db, "SELECT filename FROM songs WHERE _id = ? AND pending_delete = 0",
	  -1, &stmt, NULL);
	assert(rc == SQLITE_OK);

	sqlite3_bind_int64(stmt, 1, (sqlite_int64)id);

	if ((rc = sqlite3_step(stmt)) == SQLITE_ROW)
		ret = g_strdup(sqlite3_column_text(stmt, 0));

	sqlite3_finalize(stmt);

	return ret;
}

#if 0
static void send_more_data(SoupMessage *msg, gpointer udata);

static void client_msg_failed(SoupMessage *msg, gpointer udata)
{
	FILE *fp = udata;

	printf("client_msg_failed(...)\n");

	g_signal_handlers_disconnect_by_func(msg, send_more_data, udata);

	fclose(fp);
}

static void send_more_data(SoupMessage *msg, gpointer udata)
{
	FILE *fp = udata;
	char buf[4096];
	size_t n;

	n = fread(buf, 1, sizeof(buf), fp);

	if (n < sizeof(buf) && ferror(fp))
	{
		g_error("fread: %s", g_strerror(errno));
		soup_message_set_status(msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
	}
	else if (n > 0)
	{
		soup_message_add_chunk(msg, SOUP_BUFFER_SYSTEM_OWNED,
		  g_memdup(buf, n), n);
		soup_message_io_unpause(msg);
	}

	/* Short read, either error or EOF, as handled above. */
	if (n < sizeof(buf))
	{
		soup_message_add_final_chunk(msg);
		soup_message_io_unpause(msg);

		printf("send_more_data() done...\n");

		g_signal_handlers_disconnect_by_func(msg, send_more_data, fp);
		g_signal_handlers_disconnect_by_func(msg, client_msg_failed, fp);
		fclose(fp);
	}
}
#endif

static unsigned char *slurp_file(FILE *fp, size_t *len)
{
	GString *data;
	unsigned char buf[1024];
	size_t n;

	data = g_string_new(NULL);
	assert(data != NULL);

	while ((n = fread(buf, 1, sizeof(buf), fp)) > 0)
		g_string_append_len(data, buf, n);

	if (ferror(fp))
	{
		*len = 0;
		g_string_free(data, TRUE);
		return NULL;
	}

	*len = data->len;
	return g_string_free(data, FALSE);
}

void content_serve_request(SoupServerContext *ctx, SoupMessage *msg,
  gpointer udata)
{
	char *path;
	char *filename;
	FILE *fp;
	unsigned char *data;
	size_t datalen;

	if (soup_method_get_id(msg->method) != SOUP_METHOD_ID_GET)
	{
		soup_message_set_status(msg, SOUP_STATUS_NOT_IMPLEMENTED);
		return;
	}

	path = soup_uri_to_string(soup_message_get_uri(msg), TRUE);
	assert(path != NULL);

	if ((filename = request_to_file(path)) == NULL)
	{
		g_error("Could not find suitable deliverable for request: %s", path);
		g_free(path);
		soup_message_set_status(msg, SOUP_STATUS_BAD_REQUEST);
		return;
	}

	g_free(path);

	if ((fp = fopen(filename, "r")) == NULL)
	{
		g_critical("fopen(%s): %s", filename, g_strerror(errno));
		g_free(filename);
		soup_message_set_status(msg, SOUP_STATUS_NOT_FOUND);
		return;
	}

	printf("sending %s...\n", filename);

	/* XXX: This is VERY bad, but it seems this version of Soup doesn't
	 * offer a way to avoid it!? */
	data = slurp_file(fp, &datalen);
	assert(data != NULL);

	fclose(fp);

	g_free(filename);

	soup_message_set_status(msg, SOUP_STATUS_OK);

	soup_server_message_set_encoding(SOUP_SERVER_MESSAGE(msg),
	  SOUP_TRANSFER_CONTENT_LENGTH);

	soup_message_set_response(msg, "application/octet-stream",
	  SOUP_BUFFER_SYSTEM_OWNED, data, datalen);

#if 0
	soup_server_message_set_encoding(SOUP_SERVER_MESSAGE(msg),
	  SOUP_TRANSFER_CHUNKED);

	send_more_data(msg, fp);
	g_signal_connect(msg, "wrote_chunk", G_CALLBACK(send_more_data), fp);

	/* Disconnected once the last chunk is written; used only to catch
	 * intermediate disconnects. */
	g_signal_connect(msg, "finished", G_CALLBACK(client_msg_failed), fp);
#endif
}
